/**
 * 此文件封装了一个公用状态库，保存了一些需要跨组件通讯的变量/对象引用
 * 设计思路：
 * 变量保存在内部state中，外部通过getStore()获取系统store单例，
 * 通过使用set,get来设置,访问保存的变量
 * 通过使用addChangeEvent来监听state的变化
 *
 * @author wujianx
 */

import { JSONObject } from 'admin/schema/common';

/**
 * 通用状态库
 * 适用场景：
 * 1. 跨组件数据通信
 * 2. 变量需要长期储存，不随页面 【刷新/销毁】 而被回收的
 * example:
 * const store = getStore();
 * ---------------------------------
 * axios.config.headers.Authorization = (store.getState() as JSONObject).token;
 */
class WStore {
  // WStore所维护的变量集
  private state: JSONObject;

  // WStore数据变化触发的回调集合
  private onChangeMap: Map<string, ((val: any) => void)[]>;

  constructor(args: JSONObject) {
    const state = (this.state = args);
    const map = (this.onChangeMap = new Map());
    const keys = Object.keys(state);
    keys.forEach(key => {
      map.set(key, []);
    });
  }

  /**
   * 获得wStore中保存的变量
   */
  public get(key: string): any {
    if (!Object.keys(this.state).includes(key)) {
      console.warn(`can not find ${key} in store`);
    }
    return this.state[key];
  }

  /**
   * 更新wStore中保存的变量
   * 触发数据的onChange事件
   */
  public set(key: string, payload: any): void {
    if (!Object.keys(this.state).includes(key)) {
      console.warn(`can not find ${key} in store`);
      return;
    }
    this.state[key] = payload;
    const changeEvents = this.onChangeMap.get(key);
    changeEvents?.forEach(onChange => {
      onChange && onChange(payload);
    });
  }

  /**
   * 获得所有state，不推荐使用，推荐使用get(key)来代替getState().key
   * getState()会报错，直接改用this.state
   */
  // public getState(): JSONObject {
  //   return this.state;
  // }

  /**
   * 设置所有state，不推荐使用
   */
  public setState(val: JSONObject): void {
    if (!val) {
      return;
    }
    for (const prop in this.state) {
      if (val[prop] != null) {
        this.state[prop] = val[prop];
      }
    }
  }

  /**
   * 给数据绑定onChange事件，每个state可以同时存在多个change事件
   * 注：勿在changeEvent中修改此函数绑定的key值，会造成死循环
   * ex: 错误示范：
   * getStore().addChangeEvent('username', (username: string) => {
   *     axios.post('/xxx').then(res => { getStore().set('username', res.username) })
   * })
   */
  public addChangeEvent(key: string, onChange: (val: any) => void) {
    if (!key || !onChange) {
      return;
    }
    const events = this.onChangeMap.get(key) as ((val: any) => void)[];
    !events.find(item => item.toString() === onChange.toString()) && events.push(onChange);
    this.onChangeMap.set(key, events);
  }

  /**数据更新存入localStorage */
  public save() {
    localStorage.setItem('store', JSON.stringify(this.state));
  }
}

let wStore: WStore;

/**
 * 外界引用store全局单例的唯一方式
 * 如果wStore尚未初始化，则以state数据作为初始化数据（localStorage保存着store在销毁之前的最后一次快照，如果存在数据，初始化后还原）
 */
export const getStore = (): WStore => {
  if (wStore) {
    return wStore;
  }
  // todo:同浏览器，一个窗口退出，另外一个窗口需要刷新才能退出。store没有共享。接口请求用的token还是当前窗口之前的store信息。退出请求接口，token失效，可以解决此问题。
  // todo:1.同浏览器，多窗口操作要表现一致；2.是否需要限制一个账号同时只能登录一台电脑
  // todo:登录后，复制链接打开login页面应自动跳转进系统首页（当前有权限的第一个菜单）或者最后显示的页面
  // window.onunload = () => {
  //   localStorage.setItem("store", JSON.stringify(getStore().getState()));
  // };
  wStore = new WStore(state);
  const val = localStorage.getItem('store');
  val && wStore.setState(JSON.parse(val));
  return wStore;
};

// 为保证store状态的可管理性，所有store记录的变量需要定义key以及初始值。未在此注册则无法正常使用store.set(),store.get()
const state = {
  /**有权限的一级菜单 */
  level1Menus: [],
  /** 左侧菜单树有权限的第一个菜单 */
  firstMenu: {},
  /** 当前任务已完成的拨打数量 */
  currentTaskFinishedNum: 0,
  /** 当前外呼测试的流水号 */
  currentCallSerialNumber: '',
  // 是否全屏操作
  isFullScreen: false,
  /**温馨提示-是否已读 */
  hasReadNotice: false,
};
